EnableExplicit

;
;  Calculates the size required to display a Gadget properly.
;
;  Supported Gadgets:
;    Button, Checkbox, Option, Text, String, ComboBox, Image
;
;  Note:
;    For Gadgets with variable content (String, ComboBox), only the returned height
;    is useful, as the width will only be an absolute minimum value.
;
;  The 'Flags' parameter gives gadget flags to include in the calculation.
;  Currently only #PB_Text_Border makes a difference there.   
;

CompilerIf Defined(Max, #PB_Procedure) = 0
  Procedure Max(a, b)
    If a > b
      ProcedureReturn a
    Else
      ProcedureReturn b
    EndIf
  EndProcedure
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  Structure PB_Gadget
    *Gadget.GtkWidget
    *Container.GtkWidget
    *VT
    UserData.i
    GadgetData.i[4]
  EndStructure
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  Structure OSX_Rect
    top.w
    left.w
    bottom.w
    right.w
  EndStructure
 
  #noErr = 0
CompilerEndIf


; Stores the result in *Width\w and *Height\w
;
Procedure GetRequiredSize(Gadget, *Width.Word, *Height.Word, Flags = 0)

  CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
 
    Protected DC.i = GetDC_(GadgetID(Gadget))
    Protected oldFont = SelectObject_(DC, GetGadgetFont(Gadget))
    Protected Size.Size
 
    Select GadgetType(Gadget)
   
      Case #PB_GadgetType_Text
        Protected Text$ = RemoveString(GetGadgetText(Gadget), Chr(10))
        Protected count = CountString(Text$, Chr(13)) + 1
        Protected empty = 0
        Protected maxheight = 0
        Protected index
        For index = 1 To count
          Protected Line$ = StringField(Text$, index, Chr(13))
          If Line$ = ""
            empty + 1
          Else
            Protected LineSize.Size
            GetTextExtentPoint32_(DC, @Line$, Len(Line$), @LineSize)
            Size\cx = Max(Size\cx, LineSize\cx)
            Size\cy + LineSize\cy
            maxheight = Max(maxheight, LineSize\cy)
          EndIf
        Next index           
        Size\cy + empty * maxheight 
       
        If Flags & #PB_Text_Border
          Size\cx + GetSystemMetrics_(#SM_CXEDGE) * 2
          Size\cy + GetSystemMetrics_(#SM_CYEDGE) * 2
        Else           
          Size\cx + 2
          Size\cy + 2
        EndIf

      Case #PB_GadgetType_CheckBox, #PB_GadgetType_Option
        Text$ = GetGadgetText(Gadget)
        GetTextExtentPoint32_(DC, @Text$, Len(Text$), @Size.SIZE)
        Size\cx + 20
        Size\cy = Max(Size\cy+2, 20)
       
      Case #PB_GadgetType_Button
        Text$ = GetGadgetText(Gadget)
        GetTextExtentPoint32_(DC, @Text$, Len(Text$), @Size.SIZE)
        Size\cx + GetSystemMetrics_(#SM_CXEDGE)*2
        Size\cy = Max(Size\cy+GetSystemMetrics_(#SM_CYEDGE)*2, 24)
        Size\cx + 10
       
      Case #PB_GadgetType_String
        Text$ = GetGadgetText(Gadget) + "Hg"
        GetTextExtentPoint32_(DC, @Text$, Len(Text$), @Size.SIZE)
        Size\cx = GetSystemMetrics_(#SM_CXEDGE)*2
        Size\cy = Max(Size\cy+GetSystemMetrics_(#SM_CXEDGE)*2, 20)
       
      Case #PB_GadgetType_ComboBox
        GetTextExtentPoint32_(DC, @"Hg", 2, @Size.SIZE)
        Size\cy = Max(Size\cy + 8, 21)
        Size\cx = Size\cy 
       
      Case #PB_GadgetType_Image
        Size\cx = GadgetWidth(Gadget)
        Size\cy = GadgetHeight(Gadget)
      
      Case #PB_GadgetType_Frame3D
        Text$ = GetGadgetText(Gadget)
        GetTextExtentPoint32_(DC, @Text$, Len(Text$), @Size.SIZE)
        Size\cx + GetSystemMetrics_(#SM_CXEDGE)*2
        Size\cy = Max(Size\cy+GetSystemMetrics_(#SM_CYEDGE)*2, 24)
        Size\cx + 10
      
       
    EndSelect
   
    SelectObject_(DC, oldFont)
    ReleaseDC_(GadgetID(Gadget), DC)
    *Width\w  = Size\cx
    *Height\w = Size\cy
 
  CompilerCase #PB_OS_Linux
 
    Protected *Gadget.PB_Gadget = IsGadget(Gadget)
    Protected RealSize.GtkRequisition
    Protected Size.GtkRequisition
   
    If *Gadget And *Gadget\Container And GadgetType(Gadget) <> #PB_GadgetType_ComboBox
      gtk_widget_size_request_(*Gadget\Container, @RealSize)
      gtk_widget_set_size_request_(*Gadget\Container, -1, -1)
      gtk_widget_size_request_(*Gadget\Container, @Size)     
      gtk_widget_set_size_request_(*Gadget\Container, RealSize\Width, RealSize\Height)
    Else
      gtk_widget_size_request_(GadgetID(Gadget), @RealSize)
      gtk_widget_set_size_request_(GadgetID(Gadget), -1, -1)             
      gtk_widget_size_request_(GadgetID(Gadget), @Size)   
      gtk_widget_set_size_request_(GadgetID(Gadget), RealSize\Width, RealSize\Height)
    EndIf
   
    If GadgetType(Gadget) = #PB_GadgetType_ComboBox Or GadgetType(Gadget) = #PB_GadgetType_String
      *Width\w  = 20
    Else
      *Width\w  = Size\Width
    EndIf

    *Height\w = Size\Height   
  CompilerCase #PB_OS_MacOS
 
    Type = GadgetType(Gadget)
   
    If Type = #PB_GadgetType_Image   
      *Width\w = GadgetWidth(Gadget)
      *Height\w = GadgetHeight(Gadget)
     
    ElseIf Type = #PB_GadgetType_Text
      realwidth   = GadgetWidth(Gadget)
     
      *Width\w = 40
      *Height\w = 20     
     
      ResizeGadget(Gadget, #PB_Ignore, #PB_Ignore, 1000, #PB_Ignore)
     
      If GetBestControlRect_(GadgetID(Gadget), @Rect.OSX_Rect, @BaseLine.w) = #noErr
        Height = Rect\bottom - Rect\top
        If Height > 0
       
          Min = 0
          Max = 1000
         
          While Max > Min+2
            Mid = (Min + Max) / 2
           
            ResizeGadget(Gadget, #PB_Ignore, #PB_Ignore, Mid, #PB_Ignore)
           
            If GetBestControlRect_(GadgetID(Gadget), @Rect.OSX_Rect, @BaseLine.w) <> #noErr
              ProcedureReturn
            EndIf
           
            If Rect\bottom - Rect\top > Height
              Min = Mid
            Else
              Max = Mid
            EndIf
          Wend
         
          *Width\w = Rect\right - Rect\left + 2
          *Height\w = Max(Height, 20)
        EndIf
         
      EndIf
     
      ResizeGadget(Gadget, #PB_Ignore, #PB_Ignore, realwidth, #PB_Ignore)

    Else
      If GetBestControlRect_(GadgetID(Gadget), @Rect.OSX_Rect, @BaseLine.w) = #noErr
        *Width\w = Rect\right - Rect\left
        *Height\w = Max(Rect\bottom - Rect\top, 20)
       
        If Type = #PB_GadgetType_Button Or Type = #PB_GadgetType_String
          *Height\w = Max(*Height\w, 24)       
        EndIf       
      Else
        *Width\w = 40
        *Height\w = 20
      EndIf
     
      If Type = #PB_GadgetType_String Or Type  = #PB_GadgetType_ComboBox       
        *Width\w = 30
      EndIf
   
    EndIf
 
  CompilerEndSelect
 
EndProcedure

; convinience wrappers if only one size is needed
;
Procedure.w GetRequiredWidth(Gadget, Flags = 0)
  Protected Width.w, Height.w
  GetRequiredSize(Gadget, @Width, @Height, Flags)
  ProcedureReturn Width
EndProcedure

Procedure.w GetRequiredHeight(Gadget, Flags = 0)
  Protected Width.w, Height.w
  GetRequiredSize(Gadget, @Width, @Height, Flags)
  ProcedureReturn Height
EndProcedure 

Enumeration
  #Sizing_MinimumSize
  #Sizing_Stretch
  #Sizing_Centre
  #Sizing_Fixed
EndEnumeration

Enumeration 
  #GeneralType_Window
  #GeneralType_Container
  #GeneralType_Gadget
EndEnumeration

Structure GadgetLayout
  ParentID.i
  ParentType.b
  ID.i
  Name.s
  GeneralType.b
  Type.w
  MinWidth.w
  MinHeight.w
  FlexWidthType.b
  FlexHeightType.b
  ActualX.w
  ActualY.w
  ActualWidth.w
  ActualHeight.w
  ItemID.i
  Orientation.b
  Flags.i
EndStructure

Enumeration
  #Orientation_Horizontal
  #Orientation_Vertical
EndEnumeration

Procedure CalculateMinimumSizes(Orientation.b, List g.GadgetLayout(), ParentID.i, *AreaWidth.Word, *AreaHeight.Word, BorderTop.b = 0, BorderBottom.b = 0, BorderLeft.b = 0, BorderRight.b = 0, Spacing.b = 6)
  ResetList(g())

  Protected newWidth.w = 0
  Protected newHeight.w = 0
  
  ForEach g()
    If g()\ParentID = ParentID
      If g()\GeneralType <> #GeneralType_Gadget
        Protected ContainerWidth.w = g()\MinWidth
        Protected ContainerHeight.w = g()\MinHeight

        Protected *CurrentGadget.i = @g()
        If g()\Type = #PB_GadgetType_Frame3D
          CalculateMinimumSizes(g()\Orientation, g(), g()\ID, @ContainerWidth, @ContainerHeight, 20, 6, 6, 6)
        Else
          CalculateMinimumSizes(g()\Orientation, g(), g()\ID, @ContainerWidth, @ContainerHeight, 0, 4, 0, 4)
        EndIf
        ChangeCurrentElement(g(), *CurrentGadget)

        g()\MinWidth = ContainerWidth
        g()\MinHeight = ContainerHeight
        
        
      EndIf

      If Orientation = #Orientation_Horizontal
        newWidth = newWidth + g()\MinWidth + Spacing
        If newHeight < g()\MinHeight
          newHeight = g()\MinHeight
        EndIf
      Else
        newHeight = newHeight + g()\MinHeight + Spacing
        If newWidth < g()\MinWidth
          newWidth = g()\MinWidth
        EndIf
      EndIf
    EndIf
  Next

  ; remove the extra space at the end
  If Orientation = #Orientation_Horizontal
    newWidth = newWidth - Spacing
  Else
    newHeight = newHeight - Spacing
  EndIf

  newWidth = newWidth + BorderLeft + BorderRight
  newHeight = newHeight + BorderTop + BorderBottom
  
  If *AreaWidth\w < newWidth
    *AreaWidth\w = newWidth
  EndIf
  If *AreaHeight\w < newHeight
    *AreaHeight\w = newHeight
  EndIf
EndProcedure

Procedure OrientGadgets(Orientation.b, List g.GadgetLayout(), ParentID.i, MinimumWidth.w, MinimumHeight.w, *AreaWidth.Word, *AreaHeight.Word, BorderTop.b = 0, BorderBottom.b = 0, BorderLeft.b = 0, BorderRight.b = 0, Spacing.b = 6)
  Protected TotalMinHeight.w = MinimumHeight ;+ BorderTop + BorderBottom
  Protected TotalMinWidth.w = MinimumWidth ;+ BorderLeft + BorderRight

  If *AreaWidth\w < TotalMinWidth
    *AreaWidth\w = TotalMinWidth
  EndIf
  If *AreaHeight\w < TotalMinHeight
    *AreaHeight\w = TotalMinHeight
  EndIf

;-------------------------------------------------

  Protected RemainingAreaWidth.w = *AreaWidth\w - TotalMinWidth
  Protected RemainingAreaHeight.w = *AreaHeight\w - TotalMinHeight
  Protected InnerHeight.w = *AreaHeight\w - BorderTop - BorderBottom
  Protected InnerWidth.w = *AreaWidth\w - BorderLeft - BorderRight

;-------------------------------------------------

  Protected WidthFlexCount.w = 0
  Protected HeightFlexCount.w = 0
  Protected GadgetCount.w = 0

  ForEach g()
    If g()\ParentID = ParentID
      GadgetCount + 1
      
      Select g()\FlexWidthType
        Case #Sizing_Stretch, #Sizing_Centre
          WidthFlexCount = WidthFlexCount + 1
      EndSelect
    
      Select g()\FlexHeightType
        Case #Sizing_Stretch, #Sizing_Centre
          HeightFlexCount = HeightFlexCount + 1
      EndSelect
    EndIf
  Next

  Protected RemainingWidth.w, RemainingHeight.w
  If Orientation = #Orientation_Horizontal
    If WidthFlexCount = 0
      RemainingWidth = RemainingAreaWidth
    Else
      RemainingWidth = Int(RemainingAreaWidth / WidthFlexCount)
    EndIf
    
    RemainingHeight = InnerHeight
  Else
    If HeightFlexCount = 0
      RemainingHeight = RemainingAreaHeight
    Else
      RemainingHeight = Int(RemainingAreaHeight / HeightFlexCount)
    EndIf
    
    RemainingWidth = InnerWidth
  EndIf

;-------------------------------------------------

  Protected MaxHeight.w = 0 + BorderTop
  Protected MaxWidth.w = 0 + BorderLeft
  
  Protected ActualX.w = MaxWidth
  Protected ActualY.w = MaxHeight
  Protected t.i = 0

  ForEach g()
    If g()\ParentID = ParentID
      g()\ActualX = ActualX
      g()\ActualY = ActualY
      
      Select g()\FlexWidthType
        Case #Sizing_Stretch
          If Orientation = #Orientation_Horizontal
            If RemainingWidth > 0
              g()\ActualWidth = g()\MinWidth + RemainingWidth
            Else
              g()\ActualWidth = g()\MinWidth
            EndIf
          Else
            If g()\MinWidth < InnerWidth
              g()\ActualWidth = InnerWidth
            Else
              g()\ActualWidth = g()\MinWidth
            EndIf
          EndIf
        Case #Sizing_Centre
          g()\ActualWidth = g()\MinWidth
        Default
          g()\ActualWidth = g()\MinWidth
      EndSelect

      Select g()\FlexHeightType
        Case #Sizing_Stretch
          If Orientation = #Orientation_Vertical
            If RemainingHeight > 0
              g()\ActualHeight = g()\MinHeight + RemainingHeight
            Else
              g()\ActualHeight = g()\MinHeight
            EndIf
          Else
            If g()\MinHeight < InnerHeight
              g()\ActualHeight = InnerHeight
            Else
              g()\ActualHeight = g()\MinHeight
            EndIf
          EndIf
        Case #Sizing_Centre
          g()\ActualHeight = g()\MinHeight
        Default
          g()\ActualHeight = g()\MinHeight
      EndSelect

      If g()\GeneralType = #GeneralType_Container
        Protected ContainerWidth.w = g()\ActualWidth
        Protected ContainerHeight.w = g()\ActualHeight

        Protected *CurrentGadget.i = @g()
        If g()\Type = #PB_GadgetType_Frame3D
          OrientGadgets(g()\Orientation, g(), g()\ID, g()\MinWidth, g()\MinHeight, @ContainerWidth, @ContainerHeight, 20, 6, 6, 6)
        Else
          OrientGadgets(g()\Orientation, g(), g()\ID, g()\MinWidth, g()\MinHeight, @ContainerWidth, @ContainerHeight, 0, 4, 0, 4)
        EndIf
        ChangeCurrentElement(g(), *CurrentGadget)

        g()\ActualWidth = ContainerWidth
        g()\ActualHeight = ContainerHeight
      EndIf

      Select g()\FlexWidthType
        Case #Sizing_Centre
          If Orientation = #Orientation_Horizontal
            t = Int(((g()\ActualWidth + RemainingWidth) / 2) - (g()\ActualWidth / 2))
            g()\ActualX = g()\ActualX + t
          Else
            t = Int((InnerWidth / 2) - (g()\ActualWidth / 2))
            g()\ActualX = g()\ActualX + t
          EndIf
      EndSelect

      Select g()\FlexHeightType
        Case #Sizing_Centre
          If Orientation = #Orientation_Horizontal
            t = Int((InnerHeight / 2) - (g()\ActualHeight / 2))
            g()\ActualY = g()\ActualY + t
          Else
            t = Int(((g()\ActualHeight + RemainingHeight) / 2) - (g()\ActualHeight / 2))
            g()\ActualY = g()\ActualY + t
          EndIf
      EndSelect

      If g()\ID <> 0
        ResizeGadget(g()\ID, g()\ActualX, g()\ActualY, g()\ActualWidth, g()\ActualHeight)
      EndIf

      If Orientation = #Orientation_Horizontal
        MaxWidth = MaxWidth + Spacing + g()\ActualWidth
        If MaxHeight < g()\ActualHeight
          MaxHeight = g()\ActualHeight
        EndIf
        If g()\FlexWidthType = #Sizing_Centre
           MaxWidth = MaxWidth + RemainingWidth ; - g()\ActualWidth - Spacing - BorderLeft + BorderRight
        EndIf
        ActualX = MaxWidth
      Else
        MaxHeight = MaxHeight + Spacing + g()\ActualHeight
        If MaxWidth < g()\ActualWidth
          MaxWidth = g()\ActualWidth
        EndIf
        If g()\FlexHeightType = #Sizing_Centre
          MaxHeight = MaxHeight + RemainingHeight
        EndIf
        ActualY = MaxHeight
      EndIf

    EndIf
  Next
  
  If Orientation = #Orientation_Horizontal
    MaxWidth = MaxWidth - Spacing
  Else
    MaxHeight = MaxHeight - Spacing
  EndIf

  If *AreaWidth\w < MaxWidth + BorderRight
    *AreaWidth\w = MaxWidth + BorderRight
  EndIf
  If *AreaHeight\w < MaxHeight + BorderBottom
    *AreaHeight\w = MaxHeight + BorderBottom
  EndIf

EndProcedure

;{-
; NewList gadgets.GadgetLayout()
; 
; Define MainWindow = OpenWindow(#PB_Any, 110, 326, 620, 370, "Impure",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
; If MainWindow = 0
;   MessageRequester("Error", "Creating Window Failed!")
;   End
; EndIf
; 
; 
; 
; AddElement(gadgets())
; gadgets()\ID = TextGadget(#PB_Any, 0, 0, 0, 0, "Enter File Name Here :")
; GetRequiredSize(gadgets()\ID, @gadgets()\MinWidth, @gadgets()\MinHeight)
; gadgets()\FlexWidthType = #Sizing_MinimumSize
; gadgets()\FlexHeightType = #Sizing_Centre
; 
; AddElement(gadgets())
; gadgets()\ID = StringGadget(#PB_Any, 0, 0, 0, 0, "")
; GetRequiredSize(gadgets()\ID, @gadgets()\MinWidth, @gadgets()\MinHeight)
; gadgets()\FlexWidthType = #Sizing_Stretch
; gadgets()\FlexHeightType = #Sizing_Centre
; 
; AddElement(gadgets())
; gadgets()\ID = ButtonGadget(#PB_Any, 0, 0, 0, 0, "...")
; GetRequiredSize(gadgets()\ID, @gadgets()\MinWidth, @gadgets()\MinHeight)
; gadgets()\FlexWidthType = #Sizing_MinimumSize
; gadgets()\FlexHeightType = #Sizing_Centre
; 
; Define WinWidth.w = WindowWidth(MainWindow)
; Define WinHeight.w = WindowHeight(MainWindow)
; OrientGadgets(#Orientation_Horizontal, gadgets(), -1, @WinWidth, @WinHeight, 8, 8, 8, 8)
; ResizeWindow(MainWindow, #PB_Ignore, #PB_Ignore, WinWidth, WinHeight)
; 
; Repeat
;   Define Event  = WaitWindowEvent()
;   Define Gadget = EventGadget()
;   Define Type   = EventType()
;   Define Window = EventWindow()
; 
;   Select Event
;   Case #PB_Event_Gadget
;   Case #PB_Event_Menu
;   Case #PB_Event_SizeWindow
;     ;WinWidth = WindowWidth(MainWindow)
;     ;WinHeight = WindowHeight(MainWindow)
;     ;OrientGadgets(#Orientation_Horizontal, gadgets(), @WinWidth, @WinHeight, 8, 8, 8, 8, 4)
;     ;ResizeWindow(MainWindow, #PB_Ignore, #PB_Ignore, WinWidth, WinHeight)
;   Case #PB_Event_CloseWindow
;     Define Quit = 1
;   EndSelect
;   
; Until Quit = 1
;}
; IDE Options = PureBasic 4.40 Beta 7 (Windows - x86)
; CursorPosition = 73
; FirstLine = 53
; Folding = --
; EnableXP